#include <algorithm/analytical_model/Constants.h>
#include <algorithm/analytical_model/io/json/converter/analytical_model/HeliostatPara.h>
#include <algorithm/analytical_model/io/json/converter/analytical_model/HeliostatWithGlassPara.h>
#include <algorithm/analytical_model/io/json/converter/shared/utils/Vec2.h>
#include <utils/HasValue.h>

using namespace solar;

void solar::tag_invoke(const boost::json::value_from_tag& /*unused*/,
                       boost::json::value& json_value, const HeliostatPara& heliostat_para)
{
    auto heliostat_id = SOLAR_GET_VALUE(HeliostatPara, heliostat_id_, heliostat_para);
    auto receiver_id = SOLAR_GET_VALUE(HeliostatPara, receiver_id_, heliostat_para);
    auto mirror_area = SOLAR_GET_VALUE(HeliostatPara, mirror_area_, heliostat_para);
    auto total_area = SOLAR_GET_VALUE(HeliostatPara, total_area_, heliostat_para);
    auto reflectivity = SOLAR_GET_VALUE(HeliostatPara, reflectivity_, heliostat_para);
    auto sigma_tracking = SOLAR_GET_VALUE(HeliostatPara, sigma_tracking_, heliostat_para);
    auto sigma_slope_error = SOLAR_GET_VALUE(HeliostatPara, sigma_slope_error_, heliostat_para);
    auto heliostat_glass_para =
        SOLAR_GET_VALUE(HeliostatPara, heliostat_glass_para_, heliostat_para);
    json_value = {
        {     "heliostat_id",                               heliostat_id},
        {      "receiver_id",                                receiver_id},
        {      "mirror_area",                                mirror_area},
        {       "total_area",                                 total_area},
        {     "reflectivity",                               reflectivity},
        {   "sigma_tracking",    boost::json::value_from(sigma_tracking)},
        {"sigma_slope_error", boost::json::value_from(sigma_slope_error)}
    };
    if (solar::hasValue(heliostat_glass_para))
    {
        json_value.emplace_object()["heliostat_glass_para"] =
            boost::json::value_from(*heliostat_glass_para);
    }
}

auto solar::tag_invoke(const boost::json::value_to_tag<HeliostatPara>& /*unused*/,
                       const boost::json::value& json_value) -> HeliostatPara
{
    const auto* value_ptr = json_value.if_object();
    if (value_ptr == nullptr)
    {
        return {};
    }
    HeliostatPara result;
    if (const auto* heliostat_id_value = value_ptr->if_contains("heliostat_id");
        heliostat_id_value != nullptr)
    {
        SOLAR_SET_VALUE(HeliostatPara, heliostat_id_,
                        heliostat_id_value->to_number<std::uint64_t>(), result)
    }
    if (const auto* receiver_id_value = value_ptr->if_contains("receiver_id");
        receiver_id_value != nullptr)
    {
        SOLAR_SET_VALUE(HeliostatPara, receiver_id_, receiver_id_value->to_number<std::uint64_t>(),
                        result)
    }
    if (const auto* mirror_area_value = value_ptr->if_contains("mirror_area");
        mirror_area_value != nullptr)
    {
        SOLAR_SET_VALUE(HeliostatPara, mirror_area_, mirror_area_value->to_number<double>(), result)
    }
    if (const auto* total_area_value = value_ptr->if_contains("total_area");
        total_area_value != nullptr)
    {
        SOLAR_SET_VALUE(HeliostatPara, total_area_, total_area_value->to_number<double>(), result)
    }
    if (const auto* reflectivity_value = value_ptr->if_contains("reflectivity");
        reflectivity_value != nullptr)
    {
        SOLAR_SET_VALUE(HeliostatPara, reflectivity_, reflectivity_value->to_number<double>(),
                        result)
    }
    if (const auto* sigma_tracking_value = value_ptr->if_contains("sigma_tracking");
        sigma_tracking_value != nullptr)
    {
        SOLAR_SET_VALUE(HeliostatPara, sigma_tracking_,
                        boost::json::value_to<Vec2>(*sigma_tracking_value), result)
    }
    if (const auto* sigma_slope_error_value = value_ptr->if_contains("sigma_slope_error");
        sigma_slope_error_value != nullptr)
    {
        SOLAR_SET_VALUE(HeliostatPara, sigma_slope_error_,
                        boost::json::value_to<Vec2>(*sigma_slope_error_value), result)
    }
    if (const auto* heliostat_glass_para_value = value_ptr->if_contains("heliostat_glass_para");
        heliostat_glass_para_value != nullptr)
    {
        SOLAR_SET_VALUE(
            HeliostatPara, heliostat_glass_para_,
            std::make_shared<HeliostatWithGlassPara>(
                boost::json::value_to<HeliostatWithGlassPara>(*heliostat_glass_para_value)),
            result)
    }
    if (const auto* type_value = value_ptr->if_contains("type"); type_value != nullptr)
    {
        if (type_value->get_string() == "kWithGlass")
        {
            SOLAR_SET_VALUE(HeliostatPara, type_, HeliostatType::kWithGlass, result)
        }
    }
    return result;
}
